#include "util.h"
#include "DBFileSystem.h"

#include <boost/regex.hpp>
using namespace boost;

// module debug switch
#define DBEXECUTER_MODULE_TEST					0
#define DBEXECUTER_PRINT_OP_INFO				0
#define DBEXECUTER_PRINT_RECORD					1
#define DBEXECUTER_OPTI_COND_TREE				1
#define DBEXECUTER_USE_INDEX					1


// exe result
#define SUCCESS									0
#define ERR_NO_OPEN_DB							-1
#define ERR_SQL_TYPE							-11
#define ERR_USED_TABLE_NAME						-21
#define ERR_NO_SUCH_TABLE						-22
#define ERR_COLUMN_TYPE							-30
#define ERR_USED_COLUMN_NAME					-31
#define ERR_NO_SUCH_COLUMN						-32
#define ERR_CANNOT_BE_NULL						-33
#define ERR_NOT_MATCH_COL_TYPE					-34
#define ERR_WRONG_COLS_NUMBER					-35
#define ERR_WRONG_VALUE_NUMBER					-36
#define ERR_WRONG_COL_NAME						-37
#define ERR_OPEN_FILE_FAIL						-40
#define ERR_KEY_IS_NULL							-50
#define ERR_DUPLICATE_KEY						-51

/*
encoded record format:
-----------------------------------------------------------------------------------------------------------
|length|reserved| null bitmap	| fixed-len column values	| var-len column offset	|var-len column values|
-----------------------------------------------------------------------------------------------------------

len(length) = sizeof(short)
len(length) = RECORD_RESERVED_FIED_LEN
len(null bitmap) = (num_of_col % 8 == 0) ? num_of_col/8 : num_of_col/8 + 1;
len(fixed-len-col-val) = table.lenOfFixLenCol
len(var-len-col-off) = table.numOfVarLenCol * sizeof(short)
len(var-len-col-val) = the sum of len of var-len columns
*/

// constant for record
#define RECORD_LEN_FIELD_LEN					2
#define RECORD_RESERVED_FIED_LEN				2
#define MAX_EXE_MSG_LEN							256
#define MAX_IMPORT_LINE_LEN						512



class DBExecuter
{
public:	
	static int execute(ParseResult src,char*& result);
	static int createTable(ParseResult src, char*& result);
	static int drop(ParseResult src, char*& result);
	static int insert(ParseResult src, char*& result);
	static int update(ParseResult src, char*& result);
	static int del(ParseResult src, char*& result);
	static int select(ParseResult src, char*& result);
	static int importData(char* fileName, char* tableName);
private:	
	static ItTable getTable(string* tableName);		
	static ItColumn getColumn(ItTable table, string* columnName);
	static void getExeMsg(ParseResult* src, int exeReturn, char*& result);

	static int encodeRecord(ItTable table, list<string>* valueList, char* encodedRecord);
	static int encodeRecord(ItTable table, vector<string>* valueList, char* encodedRecord);
	static int setEncodeValue(ItTable table, ItColumn column, string* val, char* buffer, unsigned short* varValOff);
	static int setEncodeValue(Column* column, string* val, char* buffer);
	static int decodeRecord(ItTable table, const char* encodedRecord, vector<string>* valueList);
	static int getDecodeValue(ItTable table, ItColumn column, string *val, const char* buffer);
	static int getDecodeValue(ItTable table, int colNo, string *val, const char* buffer);

	static int parseCondValue(vector<ItTable>* tableList, vector<ItColumn>* columnList, int refColNo, string* originVal, bool* isCol, int* colNo, string* val);
	static int parseCondition(tree* condition, ParsedTree*& parsedCond, vector<ItTable>* tableList, vector<ItColumn>* columnList);
	static void getEssentialCond(vector<ItTable>* tableList, ParsedTree* cond, vector< list<ParsedAtomCond> >* essentialCondList);
	
	static bool matchCondition(vector<ItTable>* tableList, vector<char*>* recordList, ParsedTree* condition);
	static bool matchAtomCondValue(string* dst, string* src, int op);
	static bool matchEssentailCond(ItTable table, char* record, ParsedAtomCond* atomCond);
	static void getRelativeColNo(vector<ItTable>* tableList, int absoluteColNo, int* tableNo, int* relativeColNo);
	
	static void joinAndMatch(vector<ItTable>* tableList, vector< list<char*> >* allTableValCollection, list< vector<char*> >* matchValCollection, vector<char*>* jointRecord, int tableNo, ParsedTree* condition, vector<int>* selectColsNo);

	inline static int getLenghFieldLen()
	{
		return RECORD_LEN_FIELD_LEN;
	}

	inline static int getReservedLen()
	{
		return RECORD_RESERVED_FIED_LEN;
	}

	inline static int getNullBitmapLen(ItTable table)
	{
		int columnNum = table->columnList.size();
		return ((columnNum % 8 == 0) ? columnNum / 8 : columnNum / 8 + 1);
	}

	inline static int getHeaderLen(ItTable table)
	{
		return (getLenghFieldLen() + getReservedLen() + getNullBitmapLen(table));
	}

	inline static int getVarColOffsetFiledLen(ItTable table)
	{
		return (table->numOfVarLenCol * sizeof(short));
	}

	inline static void setNullBitmap(char* recordBuf, int pos, bool bit)
	{
		setBit(recordBuf + RECORD_LEN_FIELD_LEN + RECORD_RESERVED_FIED_LEN, pos, bit);
	}

	inline static bool getNullBitmap(const char* recordBuf, int pos)
	{
		return getBit(recordBuf + RECORD_LEN_FIELD_LEN + RECORD_RESERVED_FIED_LEN, pos);
	}
	
	inline static void setVarOffset(ItTable table, ItColumn column, char* recordBuffer, unsigned short* offset)
	{
		if (column->savePos == 0)
		{
			// the default offset is 0			
		}			
		else
		{			
			*(unsigned short *)(recordBuffer + getHeaderLen(table) +
				table->lenOfFixLenCol + column->savePos * sizeof(short)) = *offset;		
		}
	}

	inline static unsigned short getVarOffset(ItTable table, ItColumn column, const char* recordBuffer)
	{
		unsigned short offset = *(unsigned short *)(recordBuffer +
			getHeaderLen(table) + table->lenOfFixLenCol + column->savePos * sizeof(short));
		return offset;
	}

	inline static unsigned short getVarOffset(ItTable table, Column* column, const char* recordBuffer)
	{
		unsigned short offset = *(unsigned short *)(recordBuffer +
			getHeaderLen(table) + table->lenOfFixLenCol + column->savePos * sizeof(short));
		return offset;
	}

	inline static int getVarColLen(ItTable table, ItColumn column, const char* recordBuffer)
	{
		int len = 0;
		if (column->savePos < table->numOfVarLenCol - 1)
		{
			unsigned short startPos = *(unsigned short *)(recordBuffer + getHeaderLen(table)
				+ table->lenOfFixLenCol + column->savePos * sizeof(short));
			unsigned short endPos = *(unsigned short *)(recordBuffer + getHeaderLen(table)
				+ table->lenOfFixLenCol + (column->savePos + 1) * sizeof(short));
			len = endPos - startPos;
		}
		else
		{
			unsigned short totalLen = *(unsigned short *)recordBuffer;
			unsigned short startPos = *(unsigned short *)(recordBuffer + getHeaderLen(table)
				+ table->lenOfFixLenCol + column->savePos * sizeof(short))
				+(unsigned short)(getHeaderLen(table) + table->lenOfFixLenCol + getVarColOffsetFiledLen(table));
			len = totalLen - startPos;
		}		
		return len;
	}

	inline static int getVarColLen(ItTable table, Column* column, const char* recordBuffer)
	{
		int len = 0;
		if (column->savePos < table->numOfVarLenCol - 1)
		{
			unsigned short startPos = *(unsigned short *)(recordBuffer + getHeaderLen(table)
				+ table->lenOfFixLenCol + column->savePos * sizeof(short));
			unsigned short endPos = *(unsigned short *)(recordBuffer + getHeaderLen(table)
				+ table->lenOfFixLenCol + (column->savePos + 1) * sizeof(short));
			len = endPos - startPos;
		}
		else
		{
			unsigned short totalLen = *(unsigned short *)recordBuffer;
			unsigned short startPos = *(unsigned short *)(recordBuffer + getHeaderLen(table)
				+ table->lenOfFixLenCol + column->savePos * sizeof(short))
				+(unsigned short)(getHeaderLen(table) + table->lenOfFixLenCol + getVarColOffsetFiledLen(table));
			len = totalLen - startPos;
		}		
		return len;
	}

	inline static void setPrimaryKeyPos(ItTable table, char* buffer, char*& indexBeg, char*& indexEnd)
	{
		Column* column = &(table->columnList[table->primaryKeyIndex]);
		int headerLen = getHeaderLen(table);
		int varValFiledOffset = headerLen + table->lenOfFixLenCol + getVarColOffsetFiledLen(table);
		switch (column->columnType)
		{
		case INT:
			indexBeg = buffer + headerLen + column->savePos;
			indexEnd = indexBeg + sizeof(int);
			break;
		case CHAR:
			indexBeg = buffer + headerLen + column->savePos;
			if (ANSI_PADDING)
				indexEnd = indexBeg + column->length;
			else
			{
				indexEnd = indexBeg;
				while (*(indexEnd) != '\0' && indexEnd < indexBeg + column->length)
					indexEnd ++;				
			}
			break;
		case VARCHAR:
			{
				indexBeg = buffer + getVarOffset(table, column, buffer);
				indexEnd = indexBeg + getVarColLen(table, column, buffer);
			}
			break;
		}		
	}



	friend class ExecuterTestUnit;
};

class ExecuterTestUnit
{
public:
	static void printSystemTable();
	static void printTable(ItTable table);
	static void printColumn(ItColumn column);
	static void printRecord(ItTable table);
	static void printCondition(ParsedTree* condition, int offset);
	
	// pseudo file system operation
	static void create (Table& table);
	static void drop (Table& table);
	static void insert (Table& table, char* record, char* indexValueBeg, char* indexValueEnd);


	static list<Result>::iterator del (Table& table, list<Result>::iterator iterResult
		,  char* indexValueBeg, char* indexValueEnd);
	static void update (Table& table, list<Result>::iterator iterResult
		,char* record , char* indexValueBeg, char* indexValueEnd);

	static list<Result>* select (Table& table, char* indexValueBeg, char* indexValueEnd);	

};

